package org.jack.demo.formula.component;

import jdk.nashorn.api.scripting.NashornScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.apache.commons.io.IOUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.script.CompiledScript;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import java.io.IOException;
import java.io.InputStream;

@Configuration
public class FormulaNashornConfigure {

  protected static final String[] NASHORN_SCRIPT_ARGS = new String[]{"-doe", "--global-per-engine"};

  protected static final String[] FORMULA_SCRIPT_PATHS = new String[]{
      "org/jack/demo/formula/component/js/numeric.js",
      "org/jack/demo/formula/component/js/numeral.js",
      "org/jack/demo/formula/component/js/jstat.js",
      "org/jack/demo/formula/component/js/formula.js"
  };

  /**
   * 创建一个{@link NashornScriptEngine}实例
   * 利用 --global-per-engine 开启共享上下文，提升并发性能
   * 参照：https://blog.csdn.net/lff0305/article/details/78130832
   *
   * @return {@link NashornScriptEngine}实例
   */
  @Bean
  public NashornScriptEngine formulaNashornScriptEngine() {
    try {
      // 获取引擎工厂
      NashornScriptEngineFactory factory = getNashornScriptEngineFactory();
      // 创建脚本引擎
      NashornScriptEngine scriptEngine = (NashornScriptEngine) factory.getScriptEngine(NASHORN_SCRIPT_ARGS);
      // 编译formula.js以及其相关的依赖
      scriptEngine.eval(readFormulaScripts());
      return scriptEngine;
    } catch (Throwable e) {
      throw new RuntimeException("Create nashorn script engine error.", e);
    }
  }

  /**
   * 获取formula.js的脚本内容
   * 从静态常量{@link #FORMULA_SCRIPT_PATHS}中获取资源文件地址
   * 利用ClassLoader来进行读入
   * 最后利用{@link IOUtils#toString(InputStream, String)}转成字符串
   *
   * @return formula.js的脚本内容
   * @see #FORMULA_SCRIPT_PATHS
   */
  protected String readFormulaScripts() {
    try {
      StringBuilder scripts = new StringBuilder();
      for (String path : FORMULA_SCRIPT_PATHS) {
        scripts.append(IOUtils.toString(
            FormulaNashornConfigure.class.getClassLoader().getResourceAsStream(path), "UTF-8"));
      }
      return scripts.toString();
    } catch (IOException e) {
      throw new RuntimeException("Read formula scripts error.", e);
    }
  }

  /**
   * 从系统中查找{@link NashornScriptEngineFactory}实例
   * 如果未找到，则直接抛出异常
   * 如果环境是Java8，正常情况下能够找到
   *
   * @return {@link NashornScriptEngineFactory}实例
   */
  protected NashornScriptEngineFactory getNashornScriptEngineFactory() {
    NashornScriptEngineFactory factory = null;
    ScriptEngineManager manager = new ScriptEngineManager();
    for (ScriptEngineFactory item : manager.getEngineFactories()) {
      if (item instanceof NashornScriptEngineFactory) {
        factory = (NashornScriptEngineFactory) item;
        break;
      }
    }
    if (factory == null) {
      throw new RuntimeException("Could not found " + NashornScriptEngineFactory.class.getName());
    }
    return factory;
  }

}
